Skip to content

Expression Slots

Video Summary

  • In JSX, the content we put between open/close tags is treated as a static string. If we try and reference a variable, it'll print the variable name itself, rather than the value it references.
  • We can create expression slots with curly brackets ({}). Anything placed in-between curly brackets will be treated as pure JavaScript, instead of a string.
  • There aren't a lot of rules when it comes to JSX. The compilation process doesn't check if it's even valid! It's the messenger; it forwards the content along to the pure JS output.
  • Because JSX turns into React.createElement() function calls, we'll get a JavaScript syntax error if we try and place a statement in that slot. It has to be an expression.

For more information about the difference between statements and expressions, check out the “Statements Vs. Expressions” lesson 👀 from the JavaScript Primer module.

Here's the sandbox from the video above:

Code Playground

Open in CodeSandbox
import React from 'react';
import { createRoot } from 'react-dom/client';

const shoppingList = ['apple', 'banana', 'carrot'];

// This code...
const element = (
<div>
Items left to purchase: {shoppingList.length}
</div>
);

// ...is equivalent to this code:
const compiledElement = React.createElement(
'div',
{},
'Items left to purchase: ',
shoppingList.length
);

const container = document.querySelector('#root');
const root = createRoot(container);
root.render(element);

Comments in JSX

To add a comment in JSX, we use an expression slot:

const element = (
<div>
{/* Some comment! */}
</div>
);

We specifically need to use the multi-line comment syntax (/* */) instead of the single-line syntax (//). This is because the single-line syntax comments everything out, including the closing } for the expression slot!

Code snippet with a double-slash comment, not working

Attribute expression slots

We can use the same trick for dynamic attribute values!

Here's an example:

const uniqueId = 'content-wrapper';
const element = (
<main id={uniqueId}>
Hello World
</main>
);

As we saw above, the squiggly brackets ({}) allow us to create an expression slot. This time, we're creating a slot for the value of the id attribute.

Here's how it compiles:

const element = React.createElement(
'main',
{
id: uniqueId,
},
'Hello World'
);

For comparison, here's what it looks like without an expression slot, when the value for id is fixed:

const element = (
<main id="content-wrapper">
Hello World
</main>
);
const compiledElement = React.createElement(
'main',
{
id: "content-wrapper",
},
'Hello World'
);

We can use attribute expression slots whenever we need the values to be dynamic. We can put any valid JavaScript expression in here, not just variables:

const userEmail = 'sumeet@thegreat.com';
const element = (
<main id={userEmail.replace('@', '-')}>
Hello World
</main>
);
// Will get compiled as:
const compiledElement = React.createElement(
'main',
{
id: userEmail.replace('@', '-'),
},
'Hello World'
);

Note that when we compile the code, it doesn't actually get evaluated. We've written some logic which will turn that userEmail string into "sumeet-thegreat.com", replacing the @ character with a -, but that only happens when we run the code.

When JSX gets compiled to JS, we copy over everything between the { and }. We don't call any functions or run any of the logic. That happens later, when the processed JavaScript runs in the browser.

This is the distinction between compile-time (the code processing that happens before the code runs in the browser) and run-time (the code execution that happens in the browser).

Type coercion

At run-time, React will automatically convert types as needed when supplying attributes in expression slots.

For example, these two elements are identical:

// This works:
<input required="true" />
// And so does this!
<input required={true} />

In the first example, we're setting the required attribute equal to the string "true". In the second example, it's equal to the boolean attribute true. In HTML, values must be strings, and so the boolean true is converted to the string "true".

Similarly, we can pass either numbers or strings for numeric attributes:

// ✅ Valid
<input type="range" min="1" max="20" />
// ✅ Valid
<input type="range" min={1} max={20} />